#include <iostream>
#include <string>
#include <unordered_map>
#include <sys/types.h>
#include <sys/socket.h>
#include "log_pro.hpp"
#include "socket.hpp"
#include <iconv.h>

uint16_t defaultPort = 10000;
int defaultFD = -1;
const int fdMaxNum = 1024;

class Server
{
public:
    Server(int16_t port = defaultPort) : _port(port)
    {
        int opt = 1;
        setsockopt(_listenSock.getsock(), SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); // 设置服务器可以快速重启
        // 上面的设置要在bind之前，最好是创建socket的时候就进行setsockopt
        _listenSock.Bind(_port);
        _listenSock.Listen();
        for (int i = 0; i < fdMaxNum; i++) // 初始化pollfd表，让表中没有需要监视fd
        {
            _fds[i].fd = defaultFD;
            _fds[i].events = 0;
            _fds[i].revents = 0;
        }
    }

    void toAccept()
    {
        std::string clientIp;
        uint16_t clientPort;
        int fd = _listenSock.Accept(clientIp, &clientPort);
        if (fd < 0)
        {
            lg(WARNING, "accept 失败!");
            return;
        }
        // accpet成功后，等于是读listenfd成功，把读到的fd放入pollfd表中
        int i = 0;
        for (; i < fdMaxNum; i++)
        {
            if (_fds[i].fd == defaultFD) // 找到_fds中空位置，放入fd
            {
                _fds[i].fd = fd;
                _fds[i].events = POLLIN; // 设置为关心读
                lg(DEBUG, "成功接收新连接:%d", fd);
                break;
            }
        }
        if (i == fdMaxNum)
        {
            lg(WARNING, "_fds被占满了,需要扩容");
            // 可以继续写扩容代码，现在暂时不写
        }
    }

    void toResolve(int pos)
    {
        char buffer[1024];
        int fd = _fds[pos].fd;
        int n = read(_fds[pos].fd, buffer, sizeof(buffer) - 1);
        if (n < 0)
        {
            lg(WARNING, "read错误");
        }
        else if (n == 0) // 对端关闭
        {
            lg(NOTICE, "客户端:%d 断开连接", fd);

            _fds[pos].fd=defaultFD;//将_fds中的fd去掉
            _fds[pos].events=0;
            _fds[pos].revents=0;

            close(fd); // 关闭这个文件描述符断开连接
        }
        else
        { // 正常的处理情况
            buffer[n - 2] = 0;
            std::cout << "getMassage: " << buffer << std::endl;
        }
    }

    void Distribute() // 处理已经准备就绪的文件
    {
        for (int i = 0; i < fdMaxNum; i++) // 寻找出输入输出型参数fd_set中的fd
        {
            if (_fds[i].revents & POLLIN && _fds[i].fd >= 0) // 当_fds中某个文件描述符满足读要求，并且fd不违法时
            {
                if (_fds[i].fd == _listenSock.getsock())
                {
                    toAccept();
                }
                else
                {
                    // lg(DEBUG,"处理用户链接...");
                    toResolve(i); // 传递的是fd在_fds中的位置
                }
            }
        }
    }

    void start()
    {
        int listenFd = _listenSock.getsock();
        _fds[0] = {listenFd, POLLIN, 0}; // 最后一个值为0，代表不关心

        while (true) // 开始循环进行poll
        {
            int ret_poll = poll(_fds, fdMaxNum, -1);
            if (ret_poll == 0) // 代表pollfd表中没有一个文件准备就绪
            {
                lg(NOTICE, "文件未就绪...");
            }
            else if (ret_poll < 0) // 代表文件读取错误了
            {
                lg(ERROR, "文件读取错误 errno:%d %s", errno, strerror(errno));
                exit(-1);
            }
            else // 读取正确，开始处理准备就绪的文件
            {
                lg(DEBUG, "开始分配就绪文件");
                Distribute();
            }
        }
    }

private:
    Socket _listenSock;
    int16_t _port;
    /*struct*/ pollfd _fds[fdMaxNum]; // 设置pollfd表，就是要监视的文件描述符表
};